<template>
	<canvas
		ref="canvasElRef" 
		class="acf-canvas"
		@click.prevent="canvasClickEvent"
		@mousemove.prevent="canvasMouseMoveEvent">
	</canvas>
</template>

<script setup>
	
	import { ref, watch } from 'vue';
	import usePlayerConfigStore from './usePlayerConfigStore';
	import useFeedbackStore from './useFeedbackStore';
	import useOptsStore from './useOptsStore';
	import * as utilDom from '@/utils/dom';
	import * as utilMath from '@/utils/math';
	import * as $audio from './audio';
	
	const props = defineProps({
		isReady: Boolean
	})
	
	const playerConfigStore = usePlayerConfigStore();
	const feedbackStore = useFeedbackStore();
	const optsStore = useOptsStore();
	
	const canvasElRef = ref();
	let c2d, cpixelRatio, cwidth, cOffsetWidth, cheight, cOffsetHeight, cradius;
	
	const effects = {
		cursor: {
			items: []
		},
		grid: {
			items: []
		}
	}
	
	watch(() => props.isReady, (newReady, oldReady, onInvalidate) => {
		if (newReady) {
			const el = canvasElRef.value;
			cpixelRatio = utilDom.getCanvasRatio(el);
			const canvasResizeObserver = new ResizeObserver(([entry]) => {
				const { inlineSize, blockSize } = entry.borderBoxSize[0];
				el.width = inlineSize * cpixelRatio;
				el.height = blockSize * cpixelRatio;
				cOffsetWidth = inlineSize;
				cOffsetHeight = blockSize;
				cwidth = el.width;
				cheight = el.height;
				cradius = utilMath.collapse(Math.min(cwidth, cheight) * .2, [100, 240]);
			})
			canvasResizeObserver.observe(el);
			c2d = el.getContext('2d');
			c2dDraw();
			document.addEventListener('keypress', documentKeyboardEvent);
			onInvalidate(() => {
				canvasResizeObserver.disconnect();
				document.removeEventListener('keypress', documentKeyboardEvent);
			})
		}
	})
	
	function canvasClickEvent(e) {
		const cellWidth = cOffsetWidth / feedbackStore.gridCol, cellHeight = cOffsetHeight / feedbackStore.gridRow;
		const cellX = Math.floor(e.offsetX / cellWidth);
		const cellY = Math.floor(e.offsetY / cellHeight);
		if (feedbackStore.gridMap[cellY][cellX].disabled) return;
		feedbackStore.dispatchSoundByCell(cellX, cellY);
		feedbackStore.group.get(feedbackStore.gridMap[cellY][cellX].groupId).cells.forEach(cell => {
			if (!cell.disabled) {
				dispatchGrid(cell.x, cell.y, cellWidth, cellHeight, cpixelRatio);
			}
		})
	}
	
	function canvasMouseMoveEvent(e) {
		dispatchCursorTrack(e.offsetX, e.offsetY, cpixelRatio);
	}
	
	function documentKeyboardEvent(e) {
		if (optsStore.visible) return;
		const group = [...feedbackStore.group].find(g => g[1].keyboard === e.code)?.[1];
		if (group) {
			const cellWidth = cOffsetWidth / feedbackStore.gridCol, cellHeight = cOffsetHeight / feedbackStore.gridRow;
			feedbackStore.dispatchSoundByKeyboard(group.keyboard);
			feedbackStore.group.get(group.id).cells.forEach(cell => {
				if (!cell.disabled) {
					dispatchGrid(cell.x, cell.y, cellWidth, cellHeight, cpixelRatio);
				}
			})
		}
	}
	
	function dispatchGrid(cellX, cellY, cellWidth, cellHeight, pr = 1) {
		const prev = effects.grid.items.find(item => item.cellX === cellX && item.cellY === cellY);
		if (prev) {
			prev.create = new Date();
		} else {
			effects.grid.items.push({
				cellX, cellY,
				cellWidth: cellWidth * pr, 
				cellHeight: cellHeight * pr,
				create: new Date()
			})
		}
	}
	
	function dispatchCursorTrack(posX, posY, pr = 1) {
		effects.cursor.items.push({
			posX: posX * pr, 
			posY: posY * pr,
			create: new Date()
		});
	}
	
	function c2dDraw() {
		if (!props.isReady) return;
		c2d.clearRect(0, 0, cwidth, cheight);
		c2d.save();
		// drawAcTimeDomain();
		drawAcFrequency();
		// drawImg();
		drawCursor();
		drawFeedback();
		c2d.restore();
		requestAnimationFrame(c2dDraw);
	}
	
	function drawAcFrequency() {
		c2d.fillStyle = 'rgba(255, 255, 255, 0.8)';
		$audio.analyserNode.getByteFrequencyData($audio.frequencyU8A);
		const bufferLength = $audio.analyserNode.frequencyBinCount;
		// const colWidth = 180 / bufferLength * .9;
		// function d(index, deg) {
		// 	const colHeight = $audio.frequencyU8A[index] + 4;
		// 	c2d.save();
		// 	c2d.translate(cwidth / 2, cheight / 2);
		// 	c2d.rotate(deg);
		// 	c2d.fillRect(-colWidth / 2, -(cradius + 20), colWidth, -colHeight);
		// 	c2d.restore();
		// }
		// for (let i = 0; i < bufferLength; i++) {
		// 	c2d.save();
		// 	d(i, (i / bufferLength - .3) * Math.PI);
		// 	c2d.restore();
		// 	c2d.save();
		// 	d(i, (i / bufferLength + .7) * Math.PI);
		// 	c2d.restore();
		// }
		const colWidth = (cwidth / 2) / bufferLength;
		for (let index = 0; index < bufferLength; index++) {
			const colHeight = $audio.frequencyU8A[index] + 4;
			c2d.save();
			c2d.translate(colWidth * index, cheight);
			c2d.fillRect(0, 0, colWidth, -colHeight);
			c2d.restore();
			c2d.save();
			c2d.translate(cwidth - colWidth * index, cheight);
			c2d.fillRect(0, 0, -colWidth, -colHeight);
			c2d.restore();
		}
	}
	
	// function drawAcTimeDomain() {
	// 	c2d.strokeStyle = 'rgba(102, 204, 255, 0.8)';
	// 	c2d.lineWidth = 2;
	// 	$audio.analyserNode.getByteTimeDomainData($audio.timeDomainU8A);
	// 	const bufferLength = $audio.analyserNode.fftSize;
	// 	const sliceWidth = (cwidth * .3 - cradius) / (bufferLength / 2);
	// 	function sliceHeight(index) {
	// 		return cradius / 2 * $audio.timeDomainU8A[index] / 128;
	// 	}
	// 	c2d.save();
	// 	c2d.beginPath();
	// 	c2d.translate(cwidth * .2, cheight / 2 - cradius / 2);
	// 	c2d.moveTo(0, sliceHeight(0));
	// 	for (let i = 1; i < bufferLength / 2; i++) {
	// 		c2d.lineTo(sliceWidth * i, sliceHeight(i));
	// 	}
	// 	c2d.translate(cwidth * .3 + cradius, 0);
	// 	c2d.moveTo(0, sliceHeight(bufferLength / 2));
	// 	for (let i = 1; i < bufferLength / 2; i++) {
	// 		c2d.lineTo(sliceWidth * i, sliceHeight(i + bufferLength / 2));
	// 	}
	// 	c2d.stroke();
	// 	c2d.restore();
	// }
	
	// function drawImg() {
	// 	c2d.save();
	// 	c2d.beginPath();
	// 	c2d.fillStyle = '#fff';
	// 	c2d.shadowOffsetX = 0;
	// 	c2d.shadowOffsetY = 0;
	// 	c2d.shadowColor = '#888';
	// 	c2d.shadowBlur = 20;
	// 	c2d.arc(cwidth / 2, cheight / 2, cradius, 0, Math.PI * 2);
	// 	c2d.fill();
	// 	c2d.restore();
	// 	c2d.save();
	// 	c2d.strokeStyle = '#fff';
	// 	c2d.lineWidth = 8;
	// 	c2d.beginPath();
	// 	c2d.translate(cwidth / 2, cheight / 2);
	// 	c2d.rotate($audio.media.currentTime * 10 * Math.PI / 180);
	// 	c2d.arc(0, 0, cradius, 0, Math.PI * 2);
	// 	c2d.clip();
	// 	c2d.drawImage($audio.img, -cradius, -cradius, cradius * 2, cradius * 2);
	// 	c2d.stroke();
	// 	c2d.restore();
	// }
	
	function drawCursor() {
		const current = new Date();
		effects.cursor.items = effects.cursor.items.reduce((dst, next) => {
			const interval = current - next.create;
			if (interval < 3000) {
				const coefficient = 3000 - interval;
				c2d.save();
				c2d.beginPath();
				c2d.fillStyle = `rgba(155, 89, 182, ${coefficient / 3000})`;
				c2d.translate(next.posX, next.posY);
				c2d.arc(0, 0, coefficient / 1000 * 2, 0, Math.PI * 2);
				c2d.fill();
				c2d.restore();
				dst.push(next);
			}
			return dst;
		}, []);
	}
	
	function drawFeedback() {
		const current = new Date();
		effects.grid.items = effects.grid.items.reduce((dst, next) => {
			const interval = current - next.create;
			if (interval < 500) {
				const attenuation = Math.sqrt(1 - interval / 500);
				c2d.save();
				c2d.fillStyle = `rgba(255, 255, 255, ${.5 * attenuation})`;
				c2d.translate(next.cellX * next.cellWidth, next.cellY * next.cellHeight);
				c2d.fillRect(0, 0, next.cellWidth, next.cellHeight);
				c2d.restore();
				dst.push(next);
			}
			return dst;
		}, []);
	}
	
</script>

<style lang="scss" scoped>
	
	.acf-canvas {
		width: 100%;
		height: calc(100% - 60px);
	}
	
</style>